/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：事件广播于监听系统
* CreateTime：2020-04-05 19:36:56
* Version：1.0
* Modify Recorder：
 * 2020-08-05 :
 * 添加枚举类型作为泛型参数，方便维护各自内部的消息
*************************************************/

using UnityEngine;
using System;
using System.Collections.Generic;

namespace TinyToolKit
{
    public class EventCenter<TEnum> where TEnum : Enum
    {
        private static Dictionary<TEnum, Delegate> eventDic = new Dictionary<TEnum, Delegate>();

        #region 添加事件监听
        /// <summary>
        /// 添加事件监听
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="callback">事件回调</param>
        public static void Subscribe(TEnum eventType,Action callback) 
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action)eventDic[eventType] + callback;
        }

        public static void Subscribe<T>(TEnum eventType, Action<T> callback)
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action<T>)eventDic[eventType] + callback;
        }

        public static void Subscribe<T01,T02>(TEnum eventType, Action<T01, T02> callback)
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action<T01, T02>)eventDic[eventType] + callback;
        }

        public static void Subscribe<T01, T02,T03>(TEnum eventType, Action<T01, T02,T03> callback)
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action<T01, T02,T03>)eventDic[eventType] + callback;
        }

        public static void Subscribe<T01, T02, T03,T04>(TEnum eventType, Action<T01, T02, T03,T04> callback)
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action<T01, T02, T03,T04>)eventDic[eventType] + callback;
        }

        public static void Subscribe<T01, T02, T03, T04,T05>(TEnum eventType, Action<T01, T02, T03, T04,T05> callback)
        {
            OnListenerAdd(eventType, callback);

            eventDic[eventType] = (Action<T01, T02, T03, T04,T05>)eventDic[eventType] + callback;
        }
        #endregion

        #region 移除事件监听
        /// <summary>
        /// 移除监听的事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="callback">需要移除的事件</param>
        public static void Unsubscribe(TEnum eventType, Action callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }

        public static void Unsubscribe<T>(TEnum eventType, Action<T> callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action<T>)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }

        public static void Unsubscribe<T01,T02>(TEnum eventType, Action<T01, T02> callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action<T01, T02>)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }

        public static void Unsubscribe<T01, T02,T03>(TEnum eventType, Action<T01, T02,T03> callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action<T01, T02,T03>)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }

        public static void Unsubscribe<T01, T02, T03,T04>(TEnum eventType, Action<T01, T02, T03,T04> callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action<T01, T02, T03,T04>)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }

        public static void Unsubscribe<T01, T02, T03, T04,T05>(TEnum eventType, Action<T01, T02, T03, T04,T05> callback)
        {
            CheckListener(eventType, callback);
            eventDic[eventType] = (Action<T01, T02, T03, T04,T05>)eventDic[eventType] - callback;
            OnListenerRemoved(eventType);
        }
        #endregion

        #region 广播事件监听
        /// <summary>
        /// 广播监听的事件
        /// </summary>
        /// <param name="eventType">需要广播的事件类型</param>
        public static void Broadcast(TEnum eventType) 
        {
            if (eventDic.TryGetValue(eventType,out var de))
            {
                if (de is Action callback)
                    callback();
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }

        public static void Broadcast<T>(TEnum eventType,T arg)
        {
            if (eventDic.TryGetValue(eventType, out var de))
            {
                if (de is Action<T> callback)
                    callback(arg);
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }

        public static void Broadcast<T01, T02>(TEnum eventType, T01 arg01,T02 arg02)
        {
            if (eventDic.TryGetValue(eventType, out var de))
            {
                if (de is Action<T01, T02> callback)
                    callback(arg01, arg02);
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }

        public static void Broadcast<T01, T02 ,T03>(TEnum eventType, T01 arg01, T02 arg02, T03 arg03) 
        {
            if (eventDic.TryGetValue(eventType, out var de))
            {
                if (de is Action<T01, T02, T03> callback)
                    callback(arg01, arg02,arg03);
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }

        public static void Broadcast<T01, T02, T03, T04>(TEnum eventType, T01 arg01, T02 arg02, T03 arg03 ,T04 arg04)
        {
            if (eventDic.TryGetValue(eventType, out var de))
            {
                if (de is Action<T01, T02, T03, T04> callback)
                    callback(arg01, arg02, arg03, arg04);
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }

        public static void Broadcast<T01, T02, T03, T04 ,T05>(TEnum eventType, T01 arg01, T02 arg02, T03 arg03, T04 arg04, T05 arg05)
        {
            if (eventDic.TryGetValue(eventType, out var de))
            {
                if (de is Action<T01, T02, T03, T04, T05> callback)
                    callback(arg01, arg02, arg03, arg04, arg05);
                else
                    Debug.LogErrorFormat("广播事件错误：事件{0}对应的委托具有不同的类型", eventType);
            }
        }
        #endregion

        #region 辅助方法
        //添加回调事件，并检测回调函数类型是否正确
        private static void OnListenerAdd(TEnum eventType, Delegate callback)
        {
            if (eventDic.ContainsKey(eventType) == false)
                eventDic.Add(eventType, null);

            Delegate de = eventDic[eventType];
            if (de != null && de.GetType() != callback.GetType())
                TLogTool.Error("添加事件错误：尝试为事件{0}添加不同类型的委托，应该添加的委托类型为{1}，实际添加类型为{2}", eventType, de.GetType(), callback.GetType());
        }

        //检测该事件是否存在，并且检测回调类型是否正确
        private static void CheckListener(TEnum eventType, Delegate callback)
        {
            if (eventDic.ContainsKey(eventType))
            {
                Delegate de = eventDic[eventType];

                if (de == null)
                    TLogTool.Error("移除监听错误：事件" + eventType + "没有对应的委托");
                else if (de.GetType() != callback.GetType())
                    TLogTool.Error("移除监听错误：尝试为事件{0}移除不同类型的委托，应该移除的委托类型为{1}，实际移除类型为{2}", eventType, de.GetType(), callback.GetType());
            }
            else
            {
                TLogTool.Error("移除监听错误：没有对应的类型{0}", eventType);
            }
        }

        //当eventName事件的回调函数为Null，则移除该事件
        private static void OnListenerRemoved(TEnum eventType)
        {
            if (eventDic[eventType] == null)
                eventDic.Remove(eventType);
        }
        #endregion
    }
}

